/* picoc parser - parses source and executes statements */
#include "parse.h"
#include "variable.h"
#include "lex.h"
#include "table.h"
#include "type.h"
#include "platform.h"
#include "expression.h"
#ifdef DEBUGGER
int Parse::gEnableDebugger = true;
#else
int Parse::gEnableDebugger = false;
#endif

Parse::Parse() {
}
/* deallocate any memory */
void Parse::ParseCleanup(Interpreter *pc) {
    while (pc->cleanupTokenList != NULL) {
        struct CleanupTokenNode *next = pc->cleanupTokenList->next;

        Interpreter::HeapFreeMem(pc->cleanupTokenList->tokens);
        if (pc->cleanupTokenList->sourceText != NULL)
            Interpreter::HeapFreeMem((void *)pc->cleanupTokenList->sourceText);

        Interpreter::HeapFreeMem(pc->cleanupTokenList);
        pc->cleanupTokenList = next;
    }
}

/* parse a statement, but only run it if condition is true */
ParseResult Parse::ParseStatementMaybeRun(Interpreter *pc, ParseState *parser,
    int condition, int checkTrailingSemicolon) {
    if (parser->mode != kRunModeSkip && !condition) {
        enum RunMode oldMode = parser->mode;
        int result;
        parser->mode = kRunModeSkip;
        result = ParseStatement(pc, parser, checkTrailingSemicolon);
        parser->mode = oldMode;
        return (ParseResult)result;
    } else
        return ParseStatement(pc, parser, checkTrailingSemicolon);
}

/* count the number of parameters to a function or macro */
int Parse::ParseCountParams(ParseState *parser) {
    int paramCount = 0;

    LexToken token = Lex::LexGetToken(parser, NULL, true);
    if (token != kTokenCloseBracket && token != kTokenEOF) {
        /* count the number of parameters */
        paramCount++;
        while ((token = Lex::LexGetToken(parser, NULL, true)) !=
                kTokenCloseBracket && token != kTokenEOF) {
            if (token == kTokenComma)
                paramCount++;
        }
    }

    return paramCount;
}

/* parse a function definition and store it for later */
Value *Parse::ParseFunctionDefinition(Interpreter *pc, ParseState *parser,
    ValueType *returnType, char *identifier) {
    int paramCount = 0;
    char *paramIdentifier;
    LexToken token = kTokenNone;
    ValueType *paramType;
    ParseState ParamParser;
    Value *funcValue;
    Value *oldFuncValue;
    ParseState funcBody;
    //Interpreter *pc = parser->pc;

    if (pc->topStackFrame != NULL)
        Platform::ProgramFail(parser, "nested function definitions are not allowed");

    Lex::LexGetToken(parser, NULL, true);  /* open bracket */
    ParserCopy(&ParamParser, parser);
    paramCount = ParseCountParams(parser);
    if (paramCount > PARAMETER_MAX)
        Platform::ProgramFail(parser, "too many parameters (%d allowed)", PARAMETER_MAX);

    funcValue = Variable::VariableAllocValueAndData(pc, parser,
        sizeof(FuncDef) + sizeof(ValueType*)*paramCount +
        sizeof(const char*)*paramCount,
        false, NULL, true);
    funcValue->typ = &pc->functionType;
    funcValue->val->FuncDef.returnType = returnType;
    funcValue->val->FuncDef.numParams = paramCount;
    funcValue->val->FuncDef.varArgs = false;
    funcValue->val->FuncDef.paramType =
        (ValueType**)((char*)funcValue->val+sizeof(FuncDef));
    funcValue->val->FuncDef.paramName =
        (char**)((char*)funcValue->val->FuncDef.paramType +
            sizeof(ValueType*)*paramCount);

    for (paramCount = 0; paramCount < funcValue->val->FuncDef.numParams; paramCount++) {
        /* harvest the parameters into the function definition */
        if (paramCount == funcValue->val->FuncDef.numParams-1 &&
                Lex::LexGetToken(&ParamParser, NULL, false) == kTokenEllipsis) {
            /* ellipsis at end */
            funcValue->val->FuncDef.numParams--;
            funcValue->val->FuncDef.varArgs = true;
            break;
        } else {
            /* add a parameter */
            Type::TypeParse(pc, &ParamParser, &paramType, &paramIdentifier, NULL);
            if (paramType->base == kTypeVoid) {
                /* this isn't a real parameter at all - delete it */
                //paramCount--;
                funcValue->val->FuncDef.numParams--;
            } else {
                funcValue->val->FuncDef.paramType[paramCount] = paramType;
                funcValue->val->FuncDef.paramName[paramCount] = paramIdentifier;
            }
        }

        token = Lex::LexGetToken(&ParamParser, NULL, true);
        if (token != kTokenComma && paramCount < funcValue->val->FuncDef.numParams-1)
            Platform::ProgramFail(&ParamParser, "comma expected");
    }

    if (funcValue->val->FuncDef.numParams != 0 && token != kTokenCloseBracket &&
            token != kTokenComma && token != kTokenEllipsis)
        Platform::ProgramFail(&ParamParser, "bad parameter");

    if (strcmp(identifier, "main") == 0) {
        /* make sure it's int main() */
        if ( funcValue->val->FuncDef.returnType != &pc->intType &&
             funcValue->val->FuncDef.returnType != &pc->voidType )
            Platform::ProgramFail(parser, "main() should return an int or void");

        if (funcValue->val->FuncDef.numParams != 0 &&
             (funcValue->val->FuncDef.numParams != 2 ||
                funcValue->val->FuncDef.paramType[0] != &pc->intType) )
            Platform::ProgramFail(parser, "bad parameters to main()");
    }

    /* look for a function body */
    token = Lex::LexGetToken(parser, NULL, false);
    if (token == kTokenSemicolon)
        Lex::LexGetToken(parser, NULL, true);  /* it's a prototype, absorb
                                            the trailing semicolon */
    else {
        /* it's a full function definition with a body */
        if (token != kTokenLeftBrace)
            Platform::ProgramFail(parser, "bad function definition");

        Parse::ParserCopy(&funcBody, parser);
        if (Parse::ParseStatementMaybeRun(pc, parser, false, true) != kParseResultOk)
            Platform::ProgramFail(parser, "function definition expected");

        funcValue->val->FuncDef.body = funcBody;
        funcValue->val->FuncDef.body.pos = (const unsigned char *)Lex::LexCopyTokens(&funcBody, parser);

        /* is this function already in the global table? */
        if (MyTable::TableGet(&pc->globalTable, identifier, &oldFuncValue, NULL, NULL, NULL)) {
            if (oldFuncValue->val->FuncDef.body.pos == NULL) {
                /* override an old function prototype */
                (pc, MyTable::TableDelete(pc, &pc->globalTable, identifier));
            } else
                Platform::ProgramFail(parser, "'%s' is already defined", identifier);
        }
    }

    if (!MyTable::TableSet(pc, &pc->globalTable, identifier, funcValue,
                (char*)parser->fileName, parser->line, parser->characterPos))
        Platform::ProgramFail(parser, "'%s' is already defined", identifier);

    return funcValue;
}

/* parse an array initializer and assign to a variable */
int Parse::ParseArrayInitializer(Interpreter *pc, ParseState *parser, Value *newVariable,
    int doAssignment) {
    int arrayIndex = 0;
    LexToken token;
    Value *cValue;

    /* count the number of elements in the array */
    if (doAssignment && parser->mode == kRunModeRun) {
        ParseState countParser;
        int numElements;

        ParserCopy(&countParser, parser);
        numElements = ParseArrayInitializer(pc, &countParser, newVariable, false);

        if (newVariable->typ->base != kTypeArray)
            Platform::AssignFail(parser, "%t from array initializer", newVariable->typ,
                NULL, 0, 0, NULL, 0);

        if (newVariable->typ->arraySize == 0) {
            newVariable->typ = Type::TypeGetMatching(parser->pc, parser,
                newVariable->typ->fromType, newVariable->typ->base, numElements,
                newVariable->typ->identifier, true);
            Variable::VariableRealloc(pc, parser, newVariable, Type::TypeSizeValue(newVariable, false));
        }
#ifdef DEBUG_ARRAY_INITIALIZER
        PRINT_SOURCE_POS();
        printf("array size: %d \n", newVariable->typ->arraySize);
#endif
    }

    /* parse the array initializer */
    token = Lex::LexGetToken(parser, NULL, false);
    while (token != kTokenRightBrace) {
        if (Lex::LexGetToken(parser, NULL, false) == kTokenLeftBrace) {
            /* this is a sub-array initializer */
            int SubArraySize = 0;
            Value *SubArray = newVariable;
            if (parser->mode == kRunModeRun && doAssignment) {
                SubArraySize = Type::TypeSize(newVariable->typ->fromType,
                    newVariable->typ->fromType->arraySize, true);
                SubArray = Variable::VariableAllocValueFromExistingData(pc, parser,
                    newVariable->typ->fromType,
                    (union AnyValue*)(&newVariable->val->arrayMem[0] +
                        SubArraySize*arrayIndex),
                    true, newVariable);
#ifdef DEBUG_ARRAY_INITIALIZER
                int FullArraySize = Type::TypeSize(newVariable->typ,
                    newVariable->typ->arraySize, true);
                PRINT_SOURCE_POS();
                PRINT_TYPE(newVariable->typ)
                printf("[%d] subarray size: %d (full: %d,%d) \n", arrayIndex,
                    SubArraySize, FullArraySize, newVariable->typ->arraySize);
#endif
                if (arrayIndex >= newVariable->typ->arraySize)
                    Platform::ProgramFail(parser, "too many array elements");
            }
            Lex::LexGetToken(parser, NULL, true);
            ParseArrayInitializer(pc, parser, SubArray, doAssignment);
        } else {
            Value *ArrayElement = NULL;

            if (parser->mode == kRunModeRun && doAssignment) {
                ValueType * ElementType = newVariable->typ;
                int TotalSize = 1;
                int ElementSize = 0;

                /* int x[3][3] = {1,2,3,4} => handle it
                    just like int x[9] = {1,2,3,4} */
                while (ElementType->base == kTypeArray) {
                    TotalSize *= ElementType->arraySize;
                    ElementType = ElementType->fromType;

                    /* char x[10][10] = {"abc", "def"} => assign "abc" to
                        x[0], "def" to x[1] etc */
                    if (Lex::LexGetToken(parser, NULL, false) == kTokenStringConstant &&
                            ElementType->fromType->base == kTypeChar)
                        break;
                }
                ElementSize = Type::TypeSize(ElementType, ElementType->arraySize, true);
#ifdef DEBUG_ARRAY_INITIALIZER
                PRINT_SOURCE_POS();
                printf("[%d/%d] element size: %d (x%d) \n", arrayIndex, TotalSize,
                    ElementSize, ElementType->arraySize);
#endif
                if (arrayIndex >= TotalSize)
                    Platform::ProgramFail(parser, "too many array elements");
                ArrayElement = Variable::VariableAllocValueFromExistingData(pc, parser,
                    ElementType,
                    (union AnyValue*)(&newVariable->val->arrayMem[0] +
                        ElementSize*arrayIndex),
                    true, newVariable);
            }

            /* this is a normal expression initializer */
            if (!Expression::ExpressionParse(pc, parser, &cValue))
                Platform::ProgramFail(parser, "expression expected");

            if (parser->mode == kRunModeRun && doAssignment) {
                Expression::ExpressionAssign(pc, parser, ArrayElement, cValue, false, NULL, 0,
                    false);
                Variable::VariableStackPop(parser, cValue);
                Variable::VariableStackPop(parser, ArrayElement);
            }
        }

        arrayIndex++;

        token = Lex::LexGetToken(parser, NULL, false);
        if (token == kTokenComma) {
            Lex::LexGetToken(parser, NULL, true);
            token = Lex::LexGetToken(parser, NULL, false);
        } else if (token != kTokenRightBrace)
            Platform::ProgramFail(parser, "comma expected");
    }

    if (token == kTokenRightBrace)
        Lex::LexGetToken(parser, NULL, true);
    else
        Platform::ProgramFail(parser, "'}' expected");

    return arrayIndex;
}

/* assign an initial Value to a variable */
void Parse::ParseDeclarationAssignment(Interpreter *pc, ParseState *parser,
    Value *newVariable, int doAssignment) {
    Value *cValue;

    if (Lex::LexGetToken(parser, NULL, false) == kTokenLeftBrace) {
        /* this is an array initializer */
        Lex::LexGetToken(parser, NULL, true);
        ParseArrayInitializer(pc, parser, newVariable, doAssignment);
    } else {
        /* this is a normal expression initializer */
        if (!Expression::ExpressionParse(pc, parser, &cValue))
            Platform::ProgramFail(parser, "expression expected");

        if (parser->mode == kRunModeRun && doAssignment) {
            Expression::ExpressionAssign(pc, parser, newVariable, cValue, false, NULL, 0, false);
            Variable::VariableStackPop(parser, cValue);
        }
    }
}

/* declare a variable or function */
int Parse::ParseDeclaration(ParseState *parser, LexToken token) {
    int isStatic = false;
    int firstVisit = false;
    char *identifier;
    ValueType *basicType;
    ValueType *typ;
    Value *newVariable = NULL;
    Interpreter *pc = parser->pc;

    Type::TypeParseFront(parser, &basicType, &isStatic);
    do {
        Type::TypeParseIdentPart(pc, parser, basicType, &typ, &identifier);
        if ((token != kTokenVoidType && token != kTokenStructType &&
                token != kTokenUnionType && token != kTokenEnumType) &&
                identifier == pc->strEmpty)
            Platform::ProgramFail(parser, "identifier expected");

        if (identifier != pc->strEmpty) {
            /* handle function definitions */
            if (Lex::LexGetToken(parser, NULL, false) == kTokenOpenBracket)
            {
                ParseFunctionDefinition(pc, parser, typ, identifier);
                return false;
            } else {
                if (typ == &pc->voidType && identifier != pc->strEmpty)
                    Platform::ProgramFail(parser, "can't define a void variable");

                if (parser->mode == kRunModeRun || parser->mode == kRunModeGoto)
                    newVariable = Variable::VariableDefineButIgnoreIdentical(parser,
                        identifier, typ, isStatic, &firstVisit);

                if (Lex::LexGetToken(parser, NULL, false) == kTokenAssign) {
                    /* we're assigning an initial Value */
                    Lex::LexGetToken(parser, NULL, true);
                    ParseDeclarationAssignment(pc, parser, newVariable,
                        !isStatic || firstVisit);
                }
            }
        }

        token = Lex::LexGetToken(parser, NULL, false);
        if (token == kTokenComma)
            Lex::LexGetToken(parser, NULL, true);
    } while (token == kTokenComma);

    return true;
}

/* parse a #define macro definition and store it for later */
void Parse::ParseMacroDefinition(ParseState *parser) {
    char *macroNameStr;
    Value *macroName;
    Value *paramName;
    Value *macroValue;

    if (Lex::LexGetToken(parser, &macroName, true) != kTokenIdentifier)
        Platform::ProgramFail(parser, "identifier expected");

    macroNameStr = macroName->val->identifier;

    if (Lex::LexRawPeekToken(parser) == kTokenOpenMacroBracket) {
        /* it's a parameterized macro, read the parameters */
        LexToken token = Lex::LexGetToken(parser, NULL, true);
        ParseState paramParser;
        int numParams;
        int paramCount = 0;

        ParserCopy(&paramParser, parser);
        numParams = ParseCountParams(&paramParser);
        macroValue = Variable::VariableAllocValueAndData(parser->pc, parser,
            sizeof(struct MacroDef) + sizeof(const char*) * numParams,
            false, NULL, true);
        macroValue->val->MacroDef.numParams = numParams;
        macroValue->val->MacroDef.paramName = (char**)((char*)macroValue->val +
            sizeof(struct MacroDef));

        token = Lex::LexGetToken(parser, &paramName, true);

        while (token == kTokenIdentifier) {
            /* store a parameter name */
            macroValue->val->MacroDef.paramName[paramCount++] =
                paramName->val->identifier;

            /* get the trailing comma */
            token = Lex::LexGetToken(parser, NULL, true);
            if (token == kTokenComma)
                token = Lex::LexGetToken(parser, &paramName, true);

            else if (token != kTokenCloseBracket)
                Platform::ProgramFail(parser, "comma expected");
        }

        if (token != kTokenCloseBracket)
            Platform::ProgramFail(parser, "close bracket expected");
    } else {
        /* allocate a simple unparameterized macro */
        macroValue = Variable::VariableAllocValueAndData(parser->pc, parser,
            sizeof(struct MacroDef), false, NULL, true);
        macroValue->val->MacroDef.numParams = 0;
    }

    /* copy the body of the macro to execute later */
    ParserCopy(&macroValue->val->MacroDef.body, parser);
    macroValue->typ = &parser->pc->macroType;
    Lex::LexToEndOfMacro(parser);
    macroValue->val->MacroDef.body.pos =
        (unsigned char *)Lex::LexCopyTokens(&macroValue->val->MacroDef.body, parser);

    if (!MyTable::TableSet(parser->pc, &parser->pc->globalTable, macroNameStr, macroValue,
                (char *)parser->fileName, parser->line, parser->characterPos))
        Platform::ProgramFail(parser, "'%s' is already defined", macroNameStr);
}

/* copy the entire parser state */
void Parse::ParserCopy(ParseState *to, ParseState *from) {
    memcpy((void*)to, (void*)from, sizeof(*to));
}

/* copy where we're at in the parsing */
void Parse::ParserCopyPos(ParseState *to, ParseState *from) {
    to->pos = from->pos;
    to->line = from->line;
    to->hashIfLevel = from->hashIfLevel;
    to->hashIfEvaluateToLevel = from->hashIfEvaluateToLevel;
    to->characterPos = from->characterPos;
}

/* parse a "for" statement */
void Parse::ParseFor(Interpreter *pc, ParseState *parser) {
    int condition;
    ParseState preConditional;
    ParseState preIncrement;
    ParseState preStatement;
    ParseState after;

    RunMode oldMode = parser->mode;

    int prevScopeID = 0;
    int scopeID = Variable::VariableScopeBegin(parser, &prevScopeID);

    if (Lex::LexGetToken(parser, NULL, true) != kTokenOpenBracket)
        Platform::ProgramFail(parser, "'(' expected");

    if (ParseStatement(pc, parser, true) != kParseResultOk)
        Platform::ProgramFail(parser, "statement expected");

    ParserCopyPos(&preConditional, parser);
    if (Lex::LexGetToken(parser, NULL, false) == kTokenSemicolon)
        condition = true;
    else
        condition = Expression::ExpressionParseInt(pc, parser);

    if (Lex::LexGetToken(parser, NULL, true) != kTokenSemicolon)
        Platform::ProgramFail(parser, "';' expected");

    ParserCopyPos(&preIncrement, parser);
    ParseStatementMaybeRun(pc, parser, false, false);

    if (Lex::LexGetToken(parser, NULL, true) != kTokenCloseBracket)
        Platform::ProgramFail(parser, "')' expected");

    ParserCopyPos(&preStatement, parser);
    if (ParseStatementMaybeRun(pc, parser, condition, true) != kParseResultOk)
        Platform::ProgramFail(parser, "statement expected");

    if (parser->mode == kRunModeContinue && oldMode == kRunModeRun)
        parser->mode = kRunModeRun;

    ParserCopyPos(&after, parser);

    while (condition && parser->mode == kRunModeRun) {
        ParserCopyPos(parser, &preIncrement);
        ParseStatement(pc, parser, false);

        ParserCopyPos(parser, &preConditional);
        if (Lex::LexGetToken(parser, NULL, false) == kTokenSemicolon)
            condition = true;
        else
            condition = Expression::ExpressionParseInt(pc, parser);

        if (condition) {
            ParserCopyPos(parser, &preStatement);
            ParseStatement(pc, parser, true);

            if (parser->mode == kRunModeContinue)
                parser->mode = kRunModeRun;
        }
    }

    if (parser->mode == kRunModeBreak && oldMode == kRunModeRun)
        parser->mode = kRunModeRun;

    Variable::VariableScopeEnd(parser, scopeID, prevScopeID);

    ParserCopyPos(parser, &after);
}

/* parse a block of code and return what mode it returned in */
RunMode Parse::ParseBlock(Interpreter *pc, ParseState *parser, int absorbOpenBrace,
    int condition) {
    int prevScopeID = 0;
    int scopeID = Variable::VariableScopeBegin(parser, &prevScopeID);

    if (absorbOpenBrace && Lex::LexGetToken(parser, NULL, true) != kTokenLeftBrace)
        Platform::ProgramFail(parser, "'{' expected");

    if (parser->mode == kRunModeSkip || !condition) {
        /* condition failed - skip this block instead */
        enum RunMode OldMode = parser->mode;
        parser->mode = kRunModeSkip;
        while (Parse::ParseStatement(pc, parser, true) == kParseResultOk) {
        }
        parser->mode = OldMode;
    } else {
        /* just run it in its current mode */
        while (Parse::ParseStatement(pc, parser, true) == kParseResultOk) {
        }
    }

    if (Lex::LexGetToken(parser, NULL, true) != kTokenRightBrace)
        Platform::ProgramFail(parser, "'}' expected");

    Variable::VariableScopeEnd(parser, scopeID, prevScopeID);

    return parser->mode;
}

/* parse a typedef declaration */
void Parse::ParseTypedef(ParseState *parser) {
    char *typeName;
    ValueType *typ;
    ValueType **typPtr;
    Value initValue;

    Type::TypeParse(parser->pc, parser, &typ, &typeName, NULL);

    if (parser->mode == kRunModeRun) {
        typPtr = &typ;
        initValue.typ = &parser->pc->typeType;
        initValue.val = (union AnyValue*)typPtr;
        (parser->pc, parser, typeName, &initValue, NULL, false);
    }
}

/* parse a statement */
ParseResult Parse::ParseStatement(Interpreter *pc, ParseState *parser,
    int checkTrailingSemicolon) {
    int condition;
    LexToken token;
    Value *cValue;
    Value *lexerValue;
    Value *varValue = NULL;
    ParseState preState;

#ifdef DEBUGGER
    /* if we're debugging, check for a breakpoint */
    if (parser->debugMode && parser->mode == kRunModeRun)
        DebugCheckStatement(parser);
#endif

    /* take note of where we are and then grab a token to see what
        statement we have */
    ParserCopy(&preState, parser);
    token = Lex::LexGetToken(parser, &lexerValue, true);

    switch (token) {
    case kTokenEOF:
        return kParseResultEOF;
    case kTokenIdentifier:
        /* might be a typedef-typed variable declaration or it might
            be an expression */
        if ((parser->pc, lexerValue->val->identifier)) {
            (parser->pc, parser, lexerValue->val->identifier,
                &varValue);
            if (varValue->typ->base == kTypeType) {
                *parser = preState;
                ParseDeclaration(parser, token);
                checkTrailingSemicolon = false;
                break;
            }
        } else {
            /* it might be a goto label */
            LexToken NextToken = Lex::LexGetToken(parser, NULL, false);
            if (NextToken == kTokenColon) {
                /* declare the identifier as a goto label */
                Lex::LexGetToken(parser, NULL, true);
                if (parser->mode == kRunModeGoto &&
                        lexerValue->val->identifier == parser->searchGotoLabel)
                    parser->mode = kRunModeRun;
                checkTrailingSemicolon = false;
                break;
            }
        }
        /* else fallthrough to expression */
	    /* no break */
    case kTokenAsterisk:
    case kTokenAmpersand:
    case kTokenIncrement:
    case kTokenDecrement:
    case kTokenOpenBracket:
        *parser = preState;
        Expression::ExpressionParse(pc, parser, &cValue);
        if (parser->mode == kRunModeRun)
            Variable::VariableStackPop(parser, cValue);
        break;
    case kTokenLeftBrace:
        ParseBlock(pc, parser, false, true);
        checkTrailingSemicolon = false;
        break;
    case kTokenIf:
        if (Lex::LexGetToken(parser, NULL, true) != kTokenOpenBracket)
            Platform::ProgramFail(parser, "'(' expected");
        condition = Expression::ExpressionParseInt(pc, parser);
        if (Lex::LexGetToken(parser, NULL, true) != kTokenCloseBracket)
            Platform::ProgramFail(parser, "')' expected");
        if (ParseStatementMaybeRun(pc, parser, condition, true) != kParseResultOk)
            Platform::ProgramFail(parser, "statement expected");
        if (Lex::LexGetToken(parser, NULL, false) == kTokenElse) {
            Lex::LexGetToken(parser, NULL, true);
            if (ParseStatementMaybeRun(pc, parser, !condition, true) != kParseResultOk)
                Platform::ProgramFail(parser, "statement expected");
        }
        checkTrailingSemicolon = false;
        break;
    case kTokenWhile:
        {
            ParseState PreConditional;
            enum RunMode PreMode = parser->mode;
            if (Lex::LexGetToken(parser, NULL, true) != kTokenOpenBracket)
                Platform::ProgramFail(parser, "'(' expected");
            ParserCopyPos(&PreConditional, parser);
            do {
                ParserCopyPos(parser, &PreConditional);
                condition = Expression::ExpressionParseInt(pc, parser);
                if (Lex::LexGetToken(parser, NULL, true) != kTokenCloseBracket)
                    Platform::ProgramFail(parser, "')' expected");
                if (ParseStatementMaybeRun(pc, parser, condition, true) != kParseResultOk)
                    Platform::ProgramFail(parser, "statement expected");
                if (parser->mode == kRunModeContinue)
                    parser->mode = PreMode;
            } while (parser->mode == kRunModeRun && condition);
            if (parser->mode == kRunModeBreak)
                parser->mode = PreMode;
            checkTrailingSemicolon = false;
        }
        break;
    case kTokenDo:
        {
            ParseState PreStatement;
            enum RunMode PreMode = parser->mode;
            ParserCopyPos(&PreStatement, parser);
            do {
                ParserCopyPos(parser, &PreStatement);
                if (ParseStatement(pc, parser, true) != kParseResultOk)
                    Platform::ProgramFail(parser, "statement expected");
                if (parser->mode == kRunModeContinue)
                    parser->mode = PreMode;
                if (Lex::LexGetToken(parser, NULL, true) != kTokenWhile)
                    Platform::ProgramFail(parser, "'while' expected");
                if (Lex::LexGetToken(parser, NULL, true) != kTokenOpenBracket)
                    Platform::ProgramFail(parser, "'(' expected");
                condition = Expression::ExpressionParseInt(pc, parser);
                if (Lex::LexGetToken(parser, NULL, true) != kTokenCloseBracket)
                    Platform::ProgramFail(parser, "')' expected");
            } while (condition && parser->mode == kRunModeRun);
            if (parser->mode == kRunModeBreak)
                parser->mode = PreMode;
        }
        break;
    case kTokenFor:
        Parse::ParseFor(pc, parser);
        checkTrailingSemicolon = false;
        break;
    case kTokenSemicolon:
        checkTrailingSemicolon = false;
        break;
    case kTokenIntType:
    case kTokenShortType:
    case kTokenCharType:
    case kTokenLongType:
    case kTokenFloatType:
    case kTokenDoubleType:
    case kTokenVoidType:
    case kTokenStructType:
    case kTokenUnionType:
    case kTokenEnumType:
    case kTokenSignedType:
    case kTokenUnsignedType:
    case kTokenStaticType:
    case kTokenAutoType:
    case kTokenRegisterType:
    case kTokenExternType:
        *parser = preState;
        checkTrailingSemicolon = ParseDeclaration(parser, token);
        break;
    case kTokenHashDefine:
        ParseMacroDefinition(parser);
        checkTrailingSemicolon = false;
        break;
    case kTokenHashInclude:
        if (Lex::LexGetToken(parser, &lexerValue, true) != kTokenStringConstant)
            Platform::ProgramFail(parser, "\"filename.h\" expected");
        parser->pc->IncludeFile((char *)lexerValue->val->pointer);
        checkTrailingSemicolon = false;
        break;
    case kTokenSwitch:
        if (Lex::LexGetToken(parser, NULL, true) != kTokenOpenBracket)
            Platform::ProgramFail(parser, "'(' expected");
        condition = Expression::ExpressionParseInt(pc, parser);
        if (Lex::LexGetToken(parser, NULL, true) != kTokenCloseBracket)
            Platform::ProgramFail(parser, "')' expected");
        if (Lex::LexGetToken(parser, NULL, false) != kTokenLeftBrace)
            Platform::ProgramFail(parser, "'{' expected");
        {
            /* new block so we can store parser state */
            enum RunMode OldMode = parser->mode;
            int OldSearchLabel = parser->searchLabel;
            parser->mode = kRunModeCaseSearch;
            parser->searchLabel = condition;
            ParseBlock(pc, parser, true, (OldMode != kRunModeSkip) &&
                (OldMode != kRunModeReturn));
            if (parser->mode != kRunModeReturn)
                parser->mode = OldMode;
            parser->searchLabel = OldSearchLabel;
        }
        checkTrailingSemicolon = false;
        break;
    case kTokenCase:
        if (parser->mode == kRunModeCaseSearch) {
            parser->mode = kRunModeRun;
            condition = Expression::ExpressionParseInt(pc, parser);
            parser->mode = kRunModeCaseSearch;
        } else
            condition = Expression::ExpressionParseInt(pc, parser);
        if (Lex::LexGetToken(parser, NULL, true) != kTokenColon)
            Platform::ProgramFail(parser, "':' expected");
        if (parser->mode == kRunModeCaseSearch && condition == parser->searchLabel)
            parser->mode = kRunModeRun;
        checkTrailingSemicolon = false;
        break;
    case kTokenDefault:
        if (Lex::LexGetToken(parser, NULL, true) != kTokenColon)
            Platform::ProgramFail(parser, "':' expected");
        if (parser->mode == kRunModeCaseSearch)
            parser->mode = kRunModeRun;
        checkTrailingSemicolon = false;
        break;
    case kTokenBreak:
        if (parser->mode == kRunModeRun)
            parser->mode = kRunModeBreak;
        break;
    case kTokenContinue:
        if (parser->mode == kRunModeRun)
            parser->mode = kRunModeContinue;
        break;
    case kTokenReturn:
        if (parser->mode == kRunModeRun) {
            if (!parser->pc->topStackFrame ||
                    parser->pc->topStackFrame->returnValue->typ->base != kTypeVoid) {
                if (!Expression::ExpressionParse(pc, parser, &cValue))
                    Platform::ProgramFail(parser, "Value required in return");
                if (!parser->pc->topStackFrame) /* return from top-level program? */
                    Platform::PlatformExit(parser->pc, Expression::ExpressionCoerceInteger(cValue));
                else
                    Expression::ExpressionAssign(pc, parser,
                        parser->pc->topStackFrame->returnValue, cValue, true,
                        NULL, 0, false);
                Variable::VariableStackPop(parser, cValue);
            } else {
                if (Expression::ExpressionParse(pc, parser, &cValue))
                    Platform::ProgramFail(parser, "Value in return from a void function");
            }
            parser->mode = kRunModeReturn;
        }
        else
            Expression::ExpressionParse(pc, parser, &cValue);
        break;
    case kTokenTypedef:
        ParseTypedef(parser);
        break;
    case kTokenGoto:
        if (Lex::LexGetToken(parser, &lexerValue, true) != kTokenIdentifier)
            Platform::ProgramFail(parser, "identifier expected");
        if (parser->mode == kRunModeRun) {
            /* start scanning for the goto label */
            parser->searchGotoLabel = lexerValue->val->identifier;
            parser->mode = kRunModeGoto;
        }
        break;
    case kTokenDelete:
        {
            /* try it as a function or variable name to delete */
            if (Lex::LexGetToken(parser, &lexerValue, true) != kTokenIdentifier)
                Platform::ProgramFail(parser, "identifier expected");
            if (parser->mode == kRunModeRun) {
                /* delete this variable or function */
                cValue = MyTable::TableDelete(parser->pc, &parser->pc->globalTable,
                    lexerValue->val->identifier);
                if (cValue == NULL)
                    Platform::ProgramFail(parser, "'%s' is not defined",
                        lexerValue->val->identifier);

                (parser->pc, cValue);
            }
            break;
        }
    default:
        *parser = preState;
        return kParseResultError;
    }

    if (checkTrailingSemicolon) {
        if (Lex::LexGetToken(parser, NULL, true) != kTokenSemicolon)
            Platform::ProgramFail(parser, "';' expected");
    }

    return kParseResultOk;
}

/* quick scan a source file for definitions */
void Parse::PicocParse(Interpreter *pc, const char *fileName, const char *source,
    int sourceLen, int runIt, int cleanupNow, int cleanupSource,
    int enableDebugger) {
    char *RegFileName = MyTable::TableStrRegister(pc, fileName);
    ParseResult Ok;
    ParseState parser;
    CleanupTokenNode *NewCleanupNode;

    void *tokens = Lex::LexAnalyse(pc, RegFileName, source, sourceLen, NULL);

    /* allocate a cleanup node so we can clean up the tokens later */
    if (!cleanupNow) {
        NewCleanupNode = (CleanupTokenNode *)Interpreter::HeapAllocMem(sizeof(struct CleanupTokenNode));
        if (NewCleanupNode == NULL)
            Platform::ProgramFailNoParser(pc, "(PicocParse) out of memory");

        NewCleanupNode->tokens = tokens;
        if (cleanupSource)
            NewCleanupNode->sourceText = source;
        else
            NewCleanupNode->sourceText = NULL;

        NewCleanupNode->next = pc->cleanupTokenList;
        pc->cleanupTokenList = NewCleanupNode;
    }

    /* do the parsing */
    Lex::LexInitParser(pc, &parser, source, tokens, RegFileName, runIt,
        enableDebugger);

    do {
        Ok = ParseStatement(pc, &parser, true);
    } while (Ok == kParseResultOk);

    if (Ok == kParseResultError)
        Platform::ProgramFail(&parser, "parse error");

    /* clean up */
    if (cleanupNow)
        Interpreter::HeapFreeMem(tokens);
}

/* parse interactively */
void Parse::PicocParseInteractiveNoStartPrompt(Interpreter *pc, int enableDebugger) {
    ParseResult ok;
    ParseState parser;

    Lex::LexInitParser(pc, &parser, NULL, NULL, pc->strEmpty, true, enableDebugger);
    PicocPlatformSetExitPoint(pc);
    Lex::LexInteractiveClear(pc, &parser);

    do {
        Lex::LexInteractiveStatementPrompt(pc);
        ok = Parse::ParseStatement(pc, &parser, true);
        Lex::LexInteractiveCompleted(pc, &parser);

    } while (ok == kParseResultOk);

    if (ok == kParseResultError)
        Platform::ProgramFail(&parser, "parse error");

    Platform::PlatformPrintf(pc->cStdOut, "\n");
}

/* parse interactively, showing a startup message */
void Parse::PicocParseInteractive(Interpreter *pc) {
    Platform::PlatformPrintf(pc->cStdOut, INTERACTIVE_PROMPT_START);
    PicocParseInteractiveNoStartPrompt(pc, gEnableDebugger);
}
